package com.wxapp.common.util.rsa;

import com.wxapp.common.constant.pojo.vm.RSAVm;
import com.wxapp.common.util.base64.Base64;
import com.wxapp.common.util.check.CheckUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

public class RSAUtil {
    private static final Logger log = LoggerFactory.getLogger(RSAUtil.class);
    private final static String RAS = "RSA";
    private final static int PRV_SIZE = 1024;
    private final static String ENCODING = "UTF-8";
    private final static String Sha256 = "SHA256WithRSA";

    private RSAUtil() {
    }

    /**
     * Sha256签名
     */
    public static String signSha256(String content, String privateKey) {
        if (CheckUtil.isNullString(privateKey)) {
            return null;
        }
        try {
            PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));
            KeyFactory keys = KeyFactory.getInstance(RAS);
            PrivateKey priKey = keys.generatePrivate(priPKCS8);
            //获取SHA1WithRSA签名算法对象
            Signature signature = Signature.getInstance(Sha256);
            signature.initSign(priKey); //初始化私钥
            signature.update(content.getBytes(ENCODING)); //设置需要签名的数据
            byte[] signed = signature.sign();  //获得签名值（bytes类型）
            return Base64.getEncoder().encodeToString(signed);  //签名值转换成为base64格式（String类型）
        } catch (Exception ex) {
            return null;
        }
    }
    /**
     * 公钥解密
     * @param text 待解密的信息
     * @return 解密后的文本
     */
    public static String decryptByPublicKey(RSAVm rsaVm, String text) {
        try {
            X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(rsaVm.getPublicKey()));
            KeyFactory keyFactory = KeyFactory.getInstance(RAS);
            PublicKey publicKeyString = keyFactory.generatePublic(x509EncodedKeySpec);
            Cipher cipher = Cipher.getInstance(RAS);
            cipher.init(Cipher.DECRYPT_MODE, publicKeyString);
            byte[] result = cipher.doFinal(Base64.getDecoder().decode(text));
            return new String(result);
        } catch (Exception e) {
            log.error("公钥解密失败", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 私钥加密
     * @param text 待加密的信息
     * @return 加密后的文本
     */
    public static String encryptByPrivateKey(RSAVm rsaVm, String text) {
        try {
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(rsaVm.getPrivateKey()));
            KeyFactory keyFactory = KeyFactory.getInstance(RAS);
            PrivateKey privateKeyString = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
            Cipher cipher = Cipher.getInstance(RAS);
            cipher.init(Cipher.ENCRYPT_MODE, privateKeyString);
            byte[] result = cipher.doFinal(text.getBytes());
            return Base64.getEncoder().encodeToString(result);
        } catch (Exception e) {
            log.error("私钥加密失败", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 私钥解密
     * @param text 待解密文本
     * @return 解密后的文本
     */
    public static String decryptByPrivateKey(RSAVm rsaVm, String text) {
        try {
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(rsaVm.getPrivateKey()));
            KeyFactory keyFactory = KeyFactory.getInstance(RAS);
            PrivateKey privateKeyString = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
            Cipher cipher = Cipher.getInstance(RAS);
            cipher.init(Cipher.DECRYPT_MODE, privateKeyString);
            byte[] result = cipher.doFinal(Base64.getDecoder().decode(text));
            return new String(result);
        } catch (Exception e) {
            log.error("私钥加密失败", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 公钥加密
     * @param text 待加密的文本
     * @return 加密后的文本
     */
    public static String encryptByPublicKey(RSAVm rsaVm, String text) {
        try {
            X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.getDecoder().decode(rsaVm.getPublicKey()));
            KeyFactory keyFactory = KeyFactory.getInstance(RAS);
            PublicKey publicKeyString = keyFactory.generatePublic(x509EncodedKeySpec2);
            Cipher cipher = Cipher.getInstance(RAS);
            cipher.init(Cipher.ENCRYPT_MODE, publicKeyString);
            byte[] result = cipher.doFinal(text.getBytes());
            return Base64.getEncoder().encodeToString(result);
        } catch (Exception e) {
            log.error("公钥加密失败", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 构建RSA密钥对
     * @return 生成后的公私钥信息
     */
    public static RSAVm generateKeyPair() {
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RAS);
            keyPairGenerator.initialize(PRV_SIZE);
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
            RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
            String publicKeyString = Base64.getEncoder().encodeToString(rsaPublicKey.getEncoded());
            String privateKeyString = Base64.getEncoder().encodeToString(rsaPrivateKey.getEncoded());
            return new RSAVm(publicKeyString, privateKeyString);
        } catch (Exception e) {
            log.error("构建RSA密钥对失败", e);
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        //要加密的字符串
        String en = "whatadsfsdf23123ertert5476567876ghfghferASDFSDFSDFDggfhfghRTYrtgfjg";
        //获取公钥密钥
        var rsaVm = generateKeyPair();
        System.out.printf("公钥: %s\n密钥: %s%n", rsaVm.getPublicKey(), rsaVm.getPrivateKey());
        //加密
        var s = encryptByPublicKey(rsaVm, en);
        System.out.println("加密后字符串：" + s);
        //解密后字符串
        var s1 = decryptByPrivateKey(rsaVm, s);
        System.out.println("解密后字符串: " + s1);
    }
}